package store.aixx.jwt.demo.util;

import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import io.jsonwebtoken.security.SignatureException;
import lombok.RequiredArgsConstructor;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import store.aixx.jwt.demo.properties.AppProperties;

import java.security.Key;
import java.util.Date;
import java.util.Optional;
import java.util.UUID;
import java.util.stream.Collectors;

/**
 * jwt 工具类
 *
 * @author yukai
 * @since 2021年12月10日 16:52
 */
@RequiredArgsConstructor
@Component
public class JwtUtil {
    //用于签名访问令牌密钥
    public static final Key ACCESS_KEY = Keys.secretKeyFor(SignatureAlgorithm.HS512);

    // 用于签名刷新令牌的密钥
    public static final Key REFRESH_KEY = Keys.secretKeyFor(SignatureAlgorithm.HS512);

    public final AppProperties appProperties;


    public String createAccessToken(UserDetails userDetails){
        return createJwtToken(userDetails, ACCESS_KEY, appProperties.getJwt().getAccessTokenExpireTime());
    }

    public String createRefreshToken(UserDetails userDetails){
        return createJwtToken(userDetails, REFRESH_KEY, appProperties.getJwt().getRefreshTokenExpireTime());
    }

    public boolean validateAccessToken(String token){
        return validatorToken(token, ACCESS_KEY, true);
    }

    public boolean validateAccessTokenWithoutExpireation(String token){
        return validatorToken(token, ACCESS_KEY, false);
    }

    public boolean validateRefreshToken(String refreshToken){
        return validatorToken(refreshToken, REFRESH_KEY, true);
    }


    public String createAccessTokenByRefreshToken(String refreshToken){
        return parseClaimsFromToken(refreshToken, REFRESH_KEY)
                .map(claims -> Jwts.builder()
                        .setClaims(claims)
                        .setExpiration(new Date(System.currentTimeMillis() + appProperties.getJwt().getAccessTokenExpireTime()))
                        .setIssuedAt(new Date())
                        .signWith(ACCESS_KEY, SignatureAlgorithm.HS512)
                        .compact()
                )
                .orElseThrow(() -> new AccessDeniedException("访问被拒绝"));
    }

    public Optional<Claims> parseClaimsFromToken(String token, Key key){
        try {
            Claims claims = Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token).getBody();
            return Optional.of(claims);
        }catch (Exception e){
            return Optional.empty();
        }
    }


    public String createJwtToken(UserDetails userDetails, Key key, long timeToExpire){
        long now = System.currentTimeMillis();
        return Jwts.builder()
                .setId(UUID.randomUUID().toString())
                .claim("authorities", userDetails.getAuthorities().stream()
                        .map(GrantedAuthority::getAuthority)
                        .collect(Collectors.toList())
                )
                .setSubject(userDetails.getUsername())
                .setIssuedAt(new Date(now))
                .setExpiration(new Date(now + timeToExpire))
                .signWith(key, SignatureAlgorithm.HS512)
                .compact();
    }


    /**
     * 校验token
     *
     * @param token token值
     * @param key 加密密钥
     * @param isExpiredInvalid 过期是否算异常
     * @return token验证结果
     */
    public boolean validatorToken(String token, Key key, boolean isExpiredInvalid){
        try {
            Jwts.parserBuilder().setSigningKey(key).build().parse(token);
            return true;
        }catch (ExpiredJwtException e){
            return !isExpiredInvalid;
        }catch (MalformedJwtException | SignatureException | IllegalArgumentException e) {
            return false;
        }
    }



}
